from random import choice
from tkinter import ttk
from typing import Literal
from numpy import full
from fuction import *

MENU_SIZE = (300, 200)
ROOT_NAME = 'TETRIS'
REFURBISH = 200
GAME_SIZE_ONE = (610, 510)
GAME_SIZE_TWO = (1000, 510)
NEXT_BLOCK_CANVAS_SIZE_ONE = 5
NEXT_BLOCK_CANVAS_SIZE_TWO = 5


class MainMenu:
    def __init__(self):
        self.master = create_root(MENU_SIZE, title=ROOT_NAME, icon='icon//tetris.png')
        self.frame = tk.Frame(self.master, background='white')
        self.button1 = tk.Button(self.frame, text='新游戏', width=10, command=self.button1_onclick)
        self.button2 = tk.Button(self.frame, text='双人游戏', width=10, command=self.button2_onclick)
        self.button3 = tk.Button(self.frame, text='设置', width=10, command=self.button3_onclick)

    def button1_onclick(self):
        center(self.master, GAME_SIZE_ONE)
        game = Tetris(self.master, 0, 0, GAME_SIZE_ONE, next_block_size=NEXT_BLOCK_CANVAS_SIZE_ONE)
        game.start()

    def button2_onclick(self):
        center(self.master, GAME_SIZE_TWO)
        game = Tetris(self.master, 0, 0, GAME_SIZE_TWO, next_block_size=NEXT_BLOCK_CANVAS_SIZE_TWO, user=2)
        game.start()

    def button3_onclick(self):
        setting = Setting(self.master)
        setting.start()

    def start(self):
        self.frame.place(x=0, y=0, width=MENU_SIZE[0], height=MENU_SIZE[1])
        self.button1.pack(pady=10)
        self.button2.pack(pady=10)
        self.button3.pack(pady=10)
        self.master.mainloop()


class Setting:
    def __init__(self, master):
        difficulty = 0
        self.difficulties = {500: '简单', 200: '正常', 100: '困难', 50: '噩梦'}
        game_size_1 = 0
        game_size_2 = 0
        self.game_size_one = {(1000, 810): '大型', (610, 510): '正常', (460, 450): '小型'}
        self.game_size_two = {(1210, 810): '大型', (1000, 510): '正常', (610, 450): '小型'}
        for d in self.difficulties:
            if d == REFURBISH:
                break
            else:
                difficulty += 1
        for g1 in self.game_size_one:
            if g1 == GAME_SIZE_ONE:
                break
            else:
                game_size_1 += 1
        for g2 in self.game_size_two:
            if g2 == GAME_SIZE_TWO:
                break
            else:
                game_size_2 += 1
        self.master = master
        self.frame = tk.Frame(master, width=MENU_SIZE[0], height=MENU_SIZE[1])
        self.label1 = tk.Label(self.frame, text='游戏难度', width=14)
        self.combobox1 = ttk.Combobox(self.frame, values=list(self.difficulties.values()))
        self.combobox1.current(difficulty)
        self.label2 = tk.Label(self.frame, text='单人游戏大小', width=14)
        self.combobox2 = ttk.Combobox(self.frame, values=list(self.game_size_one.values()))
        self.combobox2.current(game_size_1)
        self.label3 = tk.Label(self.frame, text='双人游戏大小', width=14)
        self.combobox3 = ttk.Combobox(self.frame, values=list(self.game_size_two.values()))
        self.combobox3.current(game_size_2)
        self.button = tk.Button(self.frame, text='返回', width=10, command=self.button_onclick)

    def combobox1_onclick(self, event):
        global REFURBISH
        difficulty = self.combobox1.get()
        for d in self.difficulties:
            if self.difficulties[d] == difficulty:
                REFURBISH = d

    def combobox2_onclick(self, event):
        global GAME_SIZE_ONE, NEXT_BLOCK_CANVAS_SIZE_ONE
        g1 = self.combobox2.get()
        for g in self.game_size_one:
            if self.game_size_one[g] == g1:
                if g1 == '小型':
                    NEXT_BLOCK_CANVAS_SIZE_ONE = 4
                else:
                    NEXT_BLOCK_CANVAS_SIZE_ONE = 5
                GAME_SIZE_ONE = g

    def combobox3_onclick(self, event):
        global GAME_SIZE_TWO, NEXT_BLOCK_CANVAS_SIZE_TWO
        g2 = self.combobox3.get()
        for g in self.game_size_two:
            if self.game_size_two[g] == g2:
                if g2 == '小型':
                    NEXT_BLOCK_CANVAS_SIZE_TWO = 4
                else:
                    NEXT_BLOCK_CANVAS_SIZE_TWO = 5
                GAME_SIZE_TWO = g

    def button_onclick(self):
        self.frame.destroy()

    def start(self):
        self.frame.place(x=0, y=0, width=MENU_SIZE[0], height=MENU_SIZE[1])
        self.label1.pack()
        self.combobox1.pack(pady=5)
        self.combobox1.bind('<<ComboboxSelected>>', self.combobox1_onclick)
        self.label2.pack()
        self.combobox2.pack(pady=5)
        self.combobox2.bind('<<ComboboxSelected>>', self.combobox2_onclick)
        self.label3.pack()
        self.combobox3.pack(pady=5)
        self.combobox3.bind('<<ComboboxSelected>>', self.combobox3_onclick)
        self.button.pack(pady=5)


class _Block:
    def __init__(self, canvas: tk.Canvas, block: list | tuple, color: str, block_array, back_color, offset=None):
        self.over = False
        self.canvas = canvas
        self.block = block
        self.color = color
        self.block_array = block_array
        self.back_color = back_color
        self.column, self.row = block_array.shape
        if offset is None:
            self.offset = (self.column // 2, -2)
        else:
            self.offset = offset
        self.now_block = self.count_block()

    def count_block(self, block=None):
        if block is None:
            block = self.block
        return [(x + self.offset[0], y + self.offset[1]) for x, y in block]

    def update(self, scope: Literal['block', 'all'] = 'block'):
        if scope == 'block':
            for x, y in self.now_block:
                draw(self.canvas, x, y, self.back_color)
            self.now_block = self.count_block()
            for x, y in self.now_block:
                draw(self.canvas, x, y, self.color)
        elif scope == 'all':
            self.canvas.delete('all')
            for x in range(self.column):
                for y in range(self.row):
                    draw(self.canvas, x, y, self.block_array[x][y])
            for x, y in self.now_block:
                draw(self.canvas, x, y, self.color)

    def end_array(self):
        self.block = self.count_block()
        for x, y in self.block:
            if y >= 0:
                self.block_array[x][y] = self.color
            else:
                self.over = True
        return self.block_array


class Block(_Block):
    def __init__(self, canvas: tk.Canvas, block: list | tuple, color: str, block_array, back_color, offset=None):
        super().__init__(canvas, block, color, block_array, back_color, offset)
        self.stop = False

    def check(self, x, y):

        if 0 <= x < self.column and ((0 <= y < self.row and self.block_array[x][y] == self.back_color) or y < 0):
            return True
        else:
            return False

    def move(self, offset=(0, 1)):
        def rotate(turn_left=True):
            if turn_left:
                for x, y in self.block:
                    if not self.check(-y+self.offset[0], x+self.offset[1]):
                        rotate(False)
                        return
                self.block = [(-y, x) for x, y in self.block]
            else:
                for x, y in self.block:
                    if not self.check(y+self.offset[0], -x+self.offset[1]):
                        return
                self.block = [(y, -x) for x, y in self.block]

        def move(_offset=offset):
            _offset = (_offset[0] + self.offset[0], _offset[1] + self.offset[1])
            for x, y in self.block:
                if not self.check(x + _offset[0], y + _offset[1]):
                    if _offset == (0 + self.offset[0], 1 + self.offset[1]):
                        self.stop = True
                    return
            self.offset = _offset

        def down():
            while True:
                move((0, 1))
                if self.stop:
                    break

        if offset == (0, -1):
            rotate()
        elif offset == (0, 2):
            down()
        else:
            move()


class User:
    def __init__(self, master, *args, **kwargs):
        self.over = False
        args = get_data_from_iter(args)
        if len(args) == 2:
            self.x, self.y = (0, 0)
            self.width, self.height = args
        elif len(args) == 4:
            self.x, self.y, self.width, self.height = args
        else:
            raise ValueError("args can't be None")

        self.block_size = kwargs.get('block_size', 30)
        self.layout = kwargs.get('layout', 'left')
        self.back_color = kwargs.get('back_color', '#cccccc')
        self.next_block_size = kwargs.get('next_block_size', 5)

        self.frame1_width = self.next_block_size * self.block_size + self.width % self.block_size
        self.canvas1_width = self.width - self.frame1_width
        # 方块类型及颜色类型
        self.block_list = ([(-1, -1), (-1, 0), (0, 0), (0, -1)],
                           [(0, -2), (0, -1), (0, 0), (0, 1)],
                           [(0, -2), (0, -1), (0, 0), (1, 0)],
                           [(0, -2), (0, -1), (0, 0), (-1, 0)],
                           [(-1, -1), (0, -1), (0, 0), (1, 0)],
                           [(-1, 0), (0, 0), (0, -1), (1, -1)],
                           [(-1, 0), (0, 0), (0, -1), (1, 0)])
        self.color_list = ('red', 'green', 'yellow', 'purple', 'blue', 'brown', 'orange')

        self.command = kwargs.get('command', True)
        self.column = self.canvas1_width // self.block_size
        self.row = self.height // self.block_size
        self.run = False
        self.mark = 0
        self.block_array = full((self.column, self.row), self.back_color)
        self.next_block_array = full((self.next_block_size, self.next_block_size), self.back_color)

        self.root = master
        self.master = tk.Frame(self.root, width=self.width, height=self.height, highlightthickness=0)
        self.canvas1 = tk.Canvas(self.master, width=self.canvas1_width, height=self.height, highlightthickness=0)
        self.frame1 = tk.Frame(self.master, width=self.frame1_width, height=self.height, highlightthickness=0)
        self.frame2 = tk.Frame(self.frame1, width=80, height=60)
        self.canvas2 = tk.Canvas(self.frame1, width=self.frame1_width, height=self.frame1_width, highlightthickness=0)

        # 分数显示文本框
        self.label_var = tk.Variable()
        self.update_mark()
        self.label = tk.Label(self.frame1, textvariable=self.label_var)
        # 退出按钮
        self.button1 = tk.Button(self.frame2, text='菜单', width=10)
        # 开始结束按钮
        self.button2_var = tk.Variable()
        self.button2_var.set('开始游戏')
        self.button2 = tk.Button(self.frame2, textvariable=self.button2_var, width=10)
        # 暂停恢复按钮
        self.button3_var = tk.Variable()
        self.button3_var.set('暂停游戏')
        self.button3 = tk.Button(self.frame2, textvariable=self.button3_var, width=10, state='disabled')

        self.components = [['place', self.master, [(self.x, self.y)]],
                           ['place', self.frame1, [(self.canvas1_width, 0) if self.layout == 'left' else (0, 0)]],
                           ['place', self.canvas1, [(0, 0) if self.layout == 'left' else (self.frame1_width, 0)]],
                           ['place', self.frame2, [(self.frame1_width-80, 0)]],
                           ['place', self.canvas2, [((self.frame1_width-self.next_block_size*self.block_size)//2, self.height // 3)]],
                           ['place', self.label, [((self.frame1_width-self.next_block_size*self.block_size)//2, self.height // 3 * 2)]],
                           ['pack', self.button1, [self.button1_onclick]],
                           ['pack', self.button2, [self.button2_onclick]],
                           ['pack', self.button3, [self.button3_onclick]]]

        self.next_block: _Block = _Block(self.canvas2, self.random_block(), self.random_color(),
                                         self.next_block_array, self.back_color, (2, 2))
        self.now_block: Block = None

    def popup(self):
        popup = tk.Toplevel(self.root)
        popup.title('游戏结束')
        center(popup, MENU_SIZE)

        label = tk.Label(popup, text=f"游戏结束\n玩家的分数是：{self.mark}\n")
        label.pack()

        button = tk.Button(popup, text='退出', command=popup.destroy)
        button.pack()

    def button1_onclick(self):
        center(get_root(self.master), MENU_SIZE)
        self.master.destroy()

    def button2_onclick(self):
        if self.button2_var.get() == '开始游戏':
            self.run = True
            self.button2_var.set('结束游戏')
            self.button3.config(state='normal')
        else:
            center(get_root(self.master), MENU_SIZE)
            self.master.destroy()
            self.popup()

    def button3_onclick(self):
        if self.button3_var.get() == '暂停游戏':
            self.run = False
            self.button3_var.set('恢复游戏')
        else:
            self.run = True
            self.button3_var.set('暂停游戏')

    def update_mark(self):
        self.label_var.set(f'您的分数是：{self.mark}')

    def update_block(self):
        if self.now_block is not None:
            self.block_array = self.now_block.end_array()
            self.block_array, get_mark = modify_array_by_column(self.block_array, self.back_color)
            self.mark += (2 ** get_mark) if get_mark != 0 else 0
            self.update_mark()
            if self.now_block.over:
                self.over = True
            self.now_block = Block(self.canvas1, self.next_block.block, self.next_block.color, self.block_array,
                                   self.back_color)
        else:
            self.now_block = Block(self.canvas1, self.next_block.block, self.next_block.color, self.block_array,
                                   self.back_color)
        self.next_block = _Block(self.canvas2, self.random_block(), self.random_color(), self.next_block_array,
                                 self.back_color, offset=(2, 2))

    def random_block(self):
        return choice(self.block_list)

    def random_color(self):
        return choice(self.color_list)

    def put(self):
        for method, component, other in self.components:
            if method == 'place':
                x, y = other[0]
                component.place(x=x, y=y)
            elif method == 'pack':
                component.pack()
                if len(other) == 1 and self.command:
                    func = other[0]
                    component.config(command=func)


class Tetris:
    def __init__(self, master, *args, **kwargs):
        args = get_data_from_iter(args)
        if len(args) == 2:
            self.x, self.y = (0, 0)
            self.width, self.height = args
        elif len(args) == 4:
            self.x, self.y, self.width, self.height = args

        self.master = master
        self.block_size = kwargs.get('block_size', 30)  # 一个方格的大小
        self.user = kwargs.get('user', 1)  # 玩家数量，默认为一
        self.back_color = kwargs.get('back_color', "#cccccc")  # 显示方块的背景的颜色
        self.next_block_size = kwargs.get('next_block_size', 5)

        if self.user == 1:
            self.user0 = User(self.master, args, block_size=self.block_size, next_block_size=self.next_block_size)
        elif self.user == 2:
            self.user0 = User(self.master, self.x + self.width // 2, self.y, self.width // 2, self.height,
                              block_size=self.block_size, command=False, next_block_size=self.next_block_size)
            self.user1 = User(self.master, self.x, self.y, self.width // 2, self.height, block_size=self.block_size,
                              next_block_size=self.next_block_size)
            self.user1.components = self.user1.components[:6]
            self.user0.button1.config(command=self.button1_onclick)
            self.user0.button2.config(command=self.button2_onclick)
            self.user0.button3.config(command=self.button3_onclick)

    def popup(self):
        popup = tk.Toplevel(self.master)
        popup.title('游戏结束')
        center(popup, MENU_SIZE)
        if self.user == 1:
            label = tk.Label(popup, text=f"游戏结束\n玩家1的分数是：{self.user0.mark}\n")
        else:
            label = tk.Label(popup, text=f"游戏结束\n玩家1的分数是：{self.user0.mark}\n玩家2的分数是：{self.user1.mark}")
        label.pack()

        button = tk.Button(popup, text='退出', command=popup.destroy)
        button.pack()

    def button1_onclick(self):
        center(get_root(self.master), MENU_SIZE)
        self.user0.master.destroy()
        self.user1.master.destroy()

    def button2_onclick(self):
        if self.user0.button2_var.get() == '开始游戏':
            self.user0.run = True
            self.user1.run = True
            self.user0.button2_var.set('结束游戏')
            self.user0.button3.config(state='normal')
        else:
            center(get_root(self.master), MENU_SIZE)
            self.user0.master.destroy()
            self.user1.master.destroy()
            self.popup()

    def button3_onclick(self):
        if self.user0.button3_var.get() == '暂停游戏':
            self.user0.run = False
            self.user1.run = False
            self.user0.button3_var.set('恢复游戏')
        else:
            self.user0.run = True
            self.user1.run = True
            self.user0.button3_var.set('暂停游戏')

    def onclick(self, event):
        keysym = event.keysym
        if self.user == 1:
            if self.user0.now_block is not None:
                if keysym in ['Up', 'w', 'W']:
                    self.user0.now_block.move((0, -1))
                elif keysym in ['Down', 's', 'S']:
                    self.user0.now_block.move((0, 2))
                elif keysym in ['Left', 'a', 'A']:
                    self.user0.now_block.move((-1, 0))
                elif keysym in ['Right', 'd', 'D']:
                    self.user0.now_block.move((1, 0))
                self.user0.now_block.update()
        else:
            if self.user1.now_block is not None:
                if keysym in ['w', 'W']:
                    self.user1.now_block.move((0, -1))
                elif keysym in ['s', 'S']:
                    self.user1.now_block.move((0, 2))
                elif keysym in ['a', 'A']:
                    self.user1.now_block.move((-1, 0))
                elif keysym in ['d', 'D']:
                    self.user1.now_block.move((1, 0))
                self.user1.now_block.update()
            if self.user0.now_block is not None:
                if keysym == 'Up':
                    self.user0.now_block.move((0, -1))
                elif keysym == 'Down':
                    self.user0.now_block.move((0, 2))
                elif keysym == 'Left':
                    self.user0.now_block.move((-1, 0))
                elif keysym == 'Right':
                    self.user0.now_block.move((1, 0))
                self.user0.now_block.update()

    def mainloop(self, user: User):
        if user.run and not user.over:
            if user.now_block.stop:
                if user.now_block.offset[1] <= 0:
                    user.run = False
                user.update_block()
                user.now_block.update('all')
                user.next_block.update('all')
            else:
                user.now_block.move()
                user.now_block.update()
        if user.over:
            if self.user == 2:
                if self.user0.over and self.user1.over:
                    root = get_root(self.master)
                    center(root, MENU_SIZE)
                    self.user0.master.destroy()
                    self.user1.master.destroy()
                    self.popup()
            else:
                root = get_root(self.master)
                center(root, MENU_SIZE)
                user.master.destroy()
                self.popup()
        else:
            self.master.after(REFURBISH, lambda: self.mainloop(user))

    def start(self):
        self.user0.put()
        self.user0.update_block()
        self.user0.next_block.update('all')
        self.user0.now_block.update('all')
        self.mainloop(self.user0)
        if self.user == 2:
            self.user1.put()
            self.user1.update_block()
            self.user1.next_block.update('all')
            self.user1.now_block.update('all')
            self.mainloop(self.user1)
        root = get_root(self.master)
        root.bind('<KeyPress>', self.onclick)
        self.master.mainloop()
